package com.tbit.uqbike.util;

import java.util.*;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Created by MyWin on 2017/5/9.
 */
public class DelayUpdate<K> implements Runnable {
    private static org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(DelayUpdate.class);
    /**
     * 检测时间间隔
     */
    private int _inv;
    /**
     * 对象超时时间
     */
    private long _timeout;
    /**
     * 对象缓存集合
     */
    private LinkedHashMap<K, LrnObj> _linkMap;
    /**
     * 同步对象
     */
    private ReentrantLock _lock;
    /**
     * 运行标志
     */
    private boolean _acitve;
    private boolean _bhandle;
    /**
     *
     */
    private Semaphore _semaphore;
    private int _ibat;

    /**
     * 延迟去重更新类
     *
     * @param timeout 单位秒
     * @param inv     单位秒
     * @param ibat
     */
    public DelayUpdate(long timeout, int inv, int ibat) {
        this._ibat = ibat;
        this._bhandle = false;
        this._inv = inv;
        this._lock = new ReentrantLock();
        this._semaphore = new Semaphore(1);
        this._linkMap = new LinkedHashMap<K, LrnObj>();
        this._timeout = timeout;
        this._acitve = false;
    }

    /**
     * 延迟去重更新类
     *
     * @param timeout 单位秒
     * @param inv     单位秒
     */
    public DelayUpdate(long timeout, int inv) {
        this._ibat = Integer.MAX_VALUE;
        this._bhandle = false;
        this._inv = inv;
        this._lock = new ReentrantLock();
        this._semaphore = new Semaphore(1);
        this._linkMap = new LinkedHashMap<K, LrnObj>();
        this._timeout = timeout;
        this._acitve = false;
    }

    /**
     * 添加元素
     *
     * @param key
     * @param obj
     */
    public void addObj(K key, IDelayUpdateObj obj) {
        addObj(key, obj, true);
    }

    /**
     * 添加元素
     *
     * @param key
     * @param obj
     * @param addDo 如果是添加，是否立即执行
     */
    public void addObj(K key, IDelayUpdateObj obj, boolean addDo) {
        LrnObj lrnObj = null;
        _lock.lock();
        try {
            if (_linkMap.containsKey(key)) {
                _linkMap.get(key).updateObj(obj);
            } else {
                lrnObj = new LrnObj(obj);
                lrnObj.idDone = addDo;
                _linkMap.put(key, lrnObj);
            }
        } finally {
            _lock.unlock();
        }
        if (lrnObj != null && addDo) {
            obj.handle();
        }
    }

    /**
     * 停止服务
     *
     * @param bhandle 标识是否需要处理完缓存的数据
     */
    public void stopService(boolean bhandle) {
        _acitve = false;
        _bhandle = bhandle;
        _semaphore.release();
    }

    /**
     * @param key
     */
    public void delKey(K key) {
        _lock.lock();
        try {
            if (_linkMap.containsKey(key)) {
                _linkMap.remove(key);
            }
        } finally {
            _lock.unlock();
        }
    }

    @Override
    public void run() {
        _acitve = true;
        try {
            _semaphore.acquire();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        while (_acitve) {
            boolean flag = false;
            try {
                flag = _semaphore.tryAcquire(_inv, TimeUnit.SECONDS);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (!flag) {
                LinkedList<IDelayUpdateObj> msg = getMsg(_ibat);
                handleBusiness(msg);
            } else {
                break;
            }
        }
        if (_bhandle) {
            LinkedList<IDelayUpdateObj> msg = getMsg(-1);
            handleBusiness(msg);
        }
    }

    public void handleBusiness(LinkedList<IDelayUpdateObj> msg) {
        for (IDelayUpdateObj obj : msg) {
            try {
                obj.handle();
            } catch (Exception e) {
                logger.error("handleBusiness", e);
            }
        }
    }

    /**
     * 获取需要处理的对象
     *
     * @param max
     * @return
     */
    private LinkedList<IDelayUpdateObj> getMsg(int max) {
        LinkedList<IDelayUpdateObj> list = new LinkedList<IDelayUpdateObj>();
        Date now = new Date();
        _lock.lock();
        try {
            Iterator<Map.Entry<K, LrnObj>> iterator = _linkMap.entrySet().iterator();
            if (max < 0) {
                while (iterator.hasNext()) {
                    Map.Entry entry = iterator.next();
                    LrnObj lrnObj = (LrnObj) entry.getValue();
                    if (!lrnObj.idDone) {
                        list.add(lrnObj.data);
                    }
                }
                _linkMap.clear();
            } else {
                List<K> delList = new LinkedList<>();
                while (iterator.hasNext() && list.size() < max) {
                    Map.Entry entry = iterator.next();
                    LrnObj lrnObj = (LrnObj) entry.getValue();
                    if (DateUtil.GetTimeSpanSec(lrnObj.dt, now) > _timeout) {
                        if (!lrnObj.idDone) {
                            list.add(lrnObj.data);
                        }
                        delList.add((K) entry.getKey());
                    } else {
                        break;
                    }
                }
                for (K k : delList) {
                    _linkMap.remove(k);
                }
            }
        } finally {
            _lock.unlock();
        }
        return list;
    }

    /**
     * 内部类
     */
    class LrnObj {
        public Date dt;
        public IDelayUpdateObj data;
        public boolean idDone = false;

        public LrnObj(IDelayUpdateObj data) {
            this.data = data;
            this.dt = new Date();
        }

        public void updateObj(IDelayUpdateObj data) {
            this.data = data;
            this.idDone = false;
        }
    }
}
